#include <GL/gl.h>
#include <math.h>
#include "Vector.h"
#include "Point.h"

static const double PI = 3.14159265358979323846;

class GL {
private:
	static int texturedQuadID;
	static int triangleID;
	static int circleID;
	static int hexagonID;

	static const int INVALID_ID = 0x800000;

	GL() {
	}

public:
	static void blendWithAlpha() {
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	}

	static void disableBlending() {
		glDisable(GL_BLEND);
	}

	static void orthoProjection(const GLdouble width, const GLdouble height) {
		const GLdouble halfWidth = width / 2;
		const GLdouble halfHeight = height / 2;
		glOrtho(-halfWidth, halfWidth, -halfHeight, halfHeight, 1.0, -1.0);
	}

	static void translate(const GLfloat x, const GLfloat y) {
		glTranslatef(x, y, 0.0f);
	}

	static void translate(const GLdouble x, const GLdouble y) {
		glTranslated(x, y, 0.0);
	}

	static void translate(const Vector &translation) {
		glTranslated(translation.x, translation.y, 0.0);
	}

	static void translate(const Point &translation) {
		glTranslated(translation.x, translation.y, 0.0);
	}

	static void translate(const GLfloat x, const GLfloat y, const GLfloat z) {
		glTranslatef(x, y, z);
	}

	static void translate(const GLdouble x, const GLdouble y, const GLdouble z) {
		glTranslated(x, y, z);
	}

	static void rotateAroundX(const GLfloat angle) {
		glRotatef(angle, 1.0f, 0.0f, 0.0f);
	}

	static void rotateAroundX(const GLdouble angle) {
		glRotated(angle, 1.0, 0.0, 0.0);
	}

	static void rotateAroundY(const GLfloat angle) {
		glRotatef(angle, 0.0f, 1.0f, 0.0f);
	}

	static void rotateAroundY(const GLdouble angle) {
		glRotated(angle, 0.0, 1.0, 0.0);
	}

	static void rotateAroundZ(const GLfloat angle) {
		glRotatef(angle, 0.0f, 0.0f, 1.0f);
	}

	static void rotateAroundZ(const GLdouble angle) {
		glRotated(angle, 0.0, 0.0, 1.0);
	}

	static void rotate(const GLfloat angle, const GLfloat x,
					   const GLfloat y, const GLfloat z) {
		glRotatef(angle, x, y, z);
	}

	static void rotate(const GLdouble angle, const GLdouble x,
					   const GLdouble y, const GLdouble z) {
		glRotated(angle, x, y, z);
	}

	static void rotate(const GLfloat angle, const Vector normal) {
		glRotated(angle, normal.x, normal.y, normal.z);
	}

	static void rotate(const GLdouble angle, const Vector normal) {
		glRotated(angle, normal.x, normal.y, normal.z);
	}

	static void scaleUniformly2D(const GLfloat scaleFactor) {
		glScalef(scaleFactor, scaleFactor, 1.0f);
	}

	static void scaleUniformly2D(const GLdouble scaleFactor) {
		glScaled(scaleFactor, scaleFactor, 1.0);
	}

	static void scaleUniformly3D(const GLfloat scaleFactor) {
		glScalef(scaleFactor, scaleFactor, scaleFactor);
	}

	static void scaleUniformly3D(const GLdouble scaleFactor) {
		glScaled(scaleFactor, scaleFactor, scaleFactor);
	}

	static void scale(const GLfloat x, const GLfloat y) {
		glScalef(x, y, 1.0f);
	}

	static void scale(const GLdouble x, const GLdouble y) {
		glScaled(x, y, 1.0);
	}

	static void scale(const GLfloat x, const GLfloat y, const GLfloat z) {
		glScalef(x, y, z);
	}

	static void scale(const GLdouble x, const GLdouble y, const GLdouble z) {
		glScaled(x, y, z);
	}

	static void setColor(const GLbyte red, const GLbyte green, const GLbyte blue) {
		glColor3b(red, green, blue);
	}

	static void setColor(const GLfloat red, const GLfloat green, const GLfloat blue) {
		glColor3f(red, green, blue);
	}

	static void setColor(const GLdouble red, const GLdouble green, const GLdouble blue) {
		glColor3d(red, green, blue);
	}

	static void setColor(const GLbyte red, const GLbyte green,
						 const GLbyte blue, const GLbyte alpha) {
		glColor4b(red, green, blue, alpha);
	}

	static void setColor(const GLfloat red, const GLfloat green,
						 const GLfloat blue, const GLfloat alpha) {
		glColor4f(red, green, blue, alpha);
	}

	static void setColor(const GLdouble red, const GLdouble green,
						 const GLdouble blue, const GLdouble alpha) {
		glColor4d(red, green, blue, alpha);
	}
/*
	static void drawTexture(const Texture texture) {
		glEnable(GL_TEXTURE_2D);
		texture.bind();
		glCallList(GL::getTexturedQuad());
	}
//*/
	static void drawSquare() {
		glDisable(GL_TEXTURE_2D);
		glCallList(GL::getTexturedQuad());
	}

	static void drawTriangle() {
		glDisable(GL_TEXTURE_2D);
		glCallList(GL::getTriangle());
	}

	static void drawCircle() {
		glDisable(GL_TEXTURE_2D);
		glCallList(GL::getCircle());
	}

	static void drawHexagon() {
		glDisable(GL_TEXTURE_2D);
		glCallList(GL::getHexagon());
	}

private:
	static int getTexturedQuad() {
		if (GL::texturedQuadID == GL::INVALID_ID) {
			GL::texturedQuadID = glGenLists(1);
			glNewList(GL::texturedQuadID, GL_COMPILE);
				glBegin(GL_QUADS);
					glTexCoord2d(0.0, 1.0);
					glVertex2d(-0.5, -0.5);
					glTexCoord2d(0.0, 0.0);
					glVertex2d(-0.5, 0.5);
					glTexCoord2d(1.0, 0.0);
					glVertex2d(0.5, 0.5);
					glTexCoord2d(1.0, 1.0);
					glVertex2d(0.5, -0.5);
				glEnd();
			glEndList();
		}
		return GL::texturedQuadID;
	}

	static int getTriangle() {
		if (GL::triangleID == GL::INVALID_ID) {
			GL::triangleID = glGenLists(1);
			glNewList(GL::triangleID, GL_COMPILE);
				glBegin(GL_TRIANGLES);
					const int segments = 3;
					const double radians = 2 * PI / segments;
					for (int i = 0; i < segments; i++) {
						double cosine = cos(i * radians + (PI / 2)) * 0.5;
						double sine = sin(i * radians + (PI / 2)) * 0.5;
						glVertex2d(cosine, sine);
					}
				glEnd();
			glEndList();
		}
		return GL::triangleID;
	}

	static int getCircle() {
		if (GL::circleID == GL::INVALID_ID) {
			GL::circleID = glGenLists(1);
			glNewList(GL::circleID, GL_COMPILE);
				glBegin(GL_POLYGON);
					const int segments = 360;
					const double radians = 2 * PI / segments;
					for (int i = 0; i < segments; i++) {
						double cosine = cos(i * radians) * 0.5;
						double sine = sin(i * radians) * 0.5;
						glVertex2d(cosine, sine);
					}
				glEnd();
			glEndList();
		}
		return GL::circleID;
	}

	static int getHexagon() {
		if (GL::hexagonID == GL::INVALID_ID) {
			GL::hexagonID = glGenLists(1);
			glNewList(GL::hexagonID, GL_COMPILE);
				glBegin(GL_POLYGON);
					const int segments = 6;
					const double radians = 2 * PI / segments;
					for (int i = 0; i < segments; i++) {
						double cosine = cos(i * radians) * 0.5;
						double sine = sin(i * radians) * 0.5;
						glVertex2d(cosine, sine);
					}
				glEnd();
			glEndList();
		}
		return GL::hexagonID;
	}
};

int GL::texturedQuadID = GL::INVALID_ID;
int GL::triangleID = GL::INVALID_ID;
int GL::circleID = GL::INVALID_ID;
int GL::hexagonID = GL::INVALID_ID;
